#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <sys/stat.h>
#include <vector>
#include <fcntl.h>
#include "ERRNO.hpp"
#include <cstdlib>
#include <strings.h>
#include <cstring>
#include <fstream>
#include<unordered_map>

using namespace std;
#define SEP "\r\n"
#define SEPLEN strlen(SEP)

//加载 Content-Type的映射关系
#define Contenttxt "wwwroot/ContentType.conf"
unordered_map<string,string> Con_map;

void initContentType()
{
    ifstream in(Contenttxt);
    if(!in.is_open()) exit(OPEN_ERR);
    string inbuffer;
    while(in>>inbuffer)
    {
        int sep = inbuffer.find(":"); //每行都一定能找到
        string str1 = inbuffer.substr(0,sep);
        string str2 = inbuffer.substr(sep+1);
        Con_map[str1]= str2;
    }
}



class Request
{
public:
    void print()
    {
        cout << "---------------------------------------------------------------" << endl;
        cout << _RqLine;
        for (auto &str : _RqHeader)
            cout << str;
        cout<<"\r\n";
        cout << _body << endl;
        cout << "---------------------------------------------------------------" << endl;
    }

public:
    string _RqLine;
    vector<string> _RqHeader;
    size_t _ConLen = 0; // 正文长度
    string _body;
    string _path; // 路径
    string _parm; // 参数
};

class Response
{
public:
    string Serialize()
    {
        string out;
        out+=_StateLine;
        for(auto&str:_ResHeader)
        out+=str;
        out+=SEP;
        out+=_body;
        return out;
    }
public:
    string _StateLine;
    vector<string> _ResHeader;
    string _body;
};
void responsehandler(int sock, string clientip, uint16_t clientport, Request rq);


void Handler(int sock, uint16_t clientport, string clientip)
{
    string inbuffer;

    while (1)
    {
        char buffer[1024];
        bzero(buffer, sizeof buffer);
        int n = read(sock, buffer, sizeof buffer - 1);
        if (n == 0)
        {
            exit(0);
        }
        if (n < 0)
        {
            cerr << "read error" << endl;
            exit(READ_ERR);
        }
        buffer[n] = 0;
        inbuffer += buffer;
        // 首先读取请求行
        // 第一行
        Request rq;
        int begin = 0;
        int end = inbuffer.find(SEP, begin);
        if (end == string::npos) // 说明一行都没有
            continue;
        // 到这里说明有一行
        rq._RqLine = inbuffer.substr(begin, end + SEPLEN);
        begin = end + SEPLEN; // 新的区间开始
                              // 读取请求报头
#define ConLenStr "Content-Length: "
        while (1)
        {
            end = inbuffer.find(SEP, begin);
            if (end == begin) // 说明读到空行,也就是把报头读完了
            {
                begin = end + SEPLEN;
                break;
            }
            else
            {
                rq._RqHeader.push_back(inbuffer.substr(begin, end - begin + SEPLEN)); // 直接把SEPLEN也当作该行内容
                begin = end + SEPLEN;
                int x = rq._RqHeader.back().find(ConLenStr); // 判断是否读到正文长度字段
                if (x != string::npos)
                {
                    int i = rq._RqHeader.back().find(" ");
                    rq._ConLen = stoi(rq._RqHeader.back().substr(i+1,rq._RqHeader.back().size()-i-1-SEPLEN));
                }
                begin = end + SEPLEN;
            }
        }
        cout<<"inbuffer size"<<inbuffer.size()<<endl;
        if (rq._ConLen <= inbuffer.size() - begin + 1)
        {
            rq._body = inbuffer.substr(begin, rq._ConLen);
            // 读取到一个完整的请求
            begin += rq._ConLen;
            inbuffer.erase(inbuffer.begin(), inbuffer.begin() + begin);
            // 打印
            rq.print();

            int urlbegin = rq._RqLine.find(" ");                             // url 的前一个空格
            int urlend = rq._RqLine.rfind(" ");                              // url 的后一个空格
            string url = rq._RqLine.substr(urlbegin + 1, urlend - urlbegin-1); // url
            // 提取参数
            int sepindex = url.find("?");
            if (sepindex != string::npos)
            {
                rq._path = url.substr(0, sepindex);
                rq._parm = url.substr(sepindex + 1);
                // 打印测试
            }
            else
                rq._path = url;

            responsehandler(sock, clientip, clientport, rq);
        }
        else
        {
            continue;
        }
    }
}
#define defaultpath "./wwwroot/index.html"

void SetContent(Response&resp, string path) //填充响应的状态行和正文
{


    int fd = open(path.c_str(), O_RDONLY);
    if (fd == -1) // 说明文件打开失败
    {
#define NotFoundPath "wwwroot/NotFound.html" // Notfound字段
        SetContent(resp, NotFoundPath);
        return;
    }
    char buffer[1024];
    int n = 0;
    while (n = read(fd, buffer, sizeof buffer - 1))
    {
        buffer[n] = 0;
        resp._body += buffer;
    }

    // 状态行我们直接采取硬编码的形式了
    if (path == NotFoundPath)
    {
        resp._StateLine = "HTTP/1.1 404 NotFound\r\n";
    }
    else
        resp._StateLine = "HTTP/1.1 200 OK\r\n";
    close(fd);
    //读完正文之后我们也可以加上Content-Type
    int sep = path.rfind(".");
    if(sep!=string::npos)
    {
        string ContType = "Content-Type: ";
        ContType+=Con_map[path.substr(sep)];  // ".html"
        // cout<<"Content:"<<path.substr(sep)<<Con_map[path.substr(sep)]<<endl;
        ContType+=SEP;
        resp._ResHeader.push_back(ContType);
    }


    return ;
}

void responsehandler(int sock, string clientip, uint16_t clientport, Request rq)
{
#define rootpath "./wwwroot"             // 这里不加 / ，因为我们的 url 的资源路径中第一个字符就是 /
    string path = rootpath + rq._path; // 拼接得到资源在我们的服务器的Linux机器中的路径
    if (path.back()=='/')
    {
        path=defaultpath;
    }
    // 返回网站首页
    Response resp;
    SetContent(resp,path); // 将文件中的数据拷贝到响应的正文中,顺带将状态行也填充进去
    // 由于我们不管怎么样都填充了正文，所以在报头字段我们可以加上 Content-Length字段。
    string len = "Content-Length: ";
    int size = resp._body.size();
    len += to_string(size);
    len+=SEP;
    resp._ResHeader.push_back(len);
    //序列化
    string out = resp.Serialize();
    cout<<"out:"<<out<<endl;
    write(sock,out.c_str(),out.size());
}
